package server.connector.http;

import server.connector.RequestStream;

import java.io.IOException;
/*
封装输入流,当发送发采用分块发送时,会自动解析出分块内容。
否则,至多读取length个字节,当length < 0,无限制
当关闭流时,若未读够length个字节个字节会读够再关闭流
当流已将关闭时,再关闭会抛出IOException异常
 */
/*
2023/1/26 该类内容全部写完,后续直接使用,无需修改
by cyl
 */
public class HttpRequestStream extends RequestStream {
    protected boolean http11 = false;
    //请求体是否是分块发送(长连接没有指明content-length时,用0\r\n\r\n块来界定报文体结束)
    protected boolean chunk = false;
    protected boolean endChunk = false;
    protected byte[] chunkBuffer = null;
    protected int chunkLength = 0;
    protected int chunkPos = 0;
    public HttpRequestStream(HttpRequestImpl request, HttpResponseImpl response) {
        super(request);
        String transferEncoding = request.getHeader("Transfer-Encoding");
        http11 = "HTTP/1.1".equals(request.getProtocol());
        chunk = (transferEncoding != null) && transferEncoding.contains("chunked");
        //既没有分块发送,又没有说明报文长度,只有请求方关闭连接才能读完完整报文,相当于请求方不支持长连接,设置响应关闭长连接
        if (!chunk && length == -1) {
            response.addHeader("Connection", "close");
        }
    }

    @Override
    public void close() throws IOException {
        if (closed)
            throw new IOException("不能重复关闭流");
        if (chunk) {
            while (!endChunk) {
                int b = read();
                //对方关闭连接
                if (b < 0)
                    break;
            }
        } else {
            if (length > 0) {
                while (count < length) {
                    int b = read();
                    if (b < 0)
                        break;
                }
            }
        }
        closed = true;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        if (chunk) {
            //目前可读到的块
            int avail = chunkLength - chunkPos;
            if (avail == 0) {
                fillChunkBuffer();
            }
            avail = chunkLength - chunkPos;
            if (avail == 0)
                return -1;
            int toCpoy = avail;
            if (avail > len)
                toCpoy = len;
            System.arraycopy(chunkBuffer, chunkPos, b, off, toCpoy);
            chunkPos += toCpoy;
            return toCpoy;
        } else {
            return super.read(b, off, len);
        }
    }

    @Override
    public int read() throws IOException {
        if (closed)
            throw new IOException("流已经被关闭");
        if (chunk) {
            if (endChunk)
                return -1;
            if (chunkBuffer == null || chunkPos >= chunkLength) {
                if (!fillChunkBuffer())
                    return -1;
            }
            return chunkBuffer[chunkPos++] & 0xff;
        } else {
            return super.read();
        }
    }
    private synchronized boolean fillChunkBuffer() throws IOException{
        chunkPos = 0;
        try {
            String numberValue = readLineFromStream();
            if (numberValue != null)
                numberValue = numberValue.trim();
            //获取块长度
            chunkLength = Integer.parseInt(numberValue, 16);
        } catch (NumberFormatException e) {
            chunkLength = 0;
            chunk = false;
            this.close();
            return false;
        }
        //遇到终止块
        if (chunkLength == 0) {
            String line = readLineFromStream();
            while (!"".equals(line)) {
                line = readLineFromStream();
            }
            endChunk = true;
            return false;
        } else {
            //扩充缓冲区
            if (chunkBuffer == null || chunkLength > chunkBuffer.length) {
                chunkBuffer = new byte[chunkLength];
            }
            //要到到缓冲区的位置索引
            int nbRead = 0;
            //当前读了多少字节
            int currentRead = 0;
            while (nbRead < chunkLength) {
                try {
                    currentRead = stream.read(chunkBuffer, nbRead, chunkLength - nbRead);
                } catch (Throwable t) {
                    t.printStackTrace();
                    throw new IOException();
                }
                //对端关闭流
                if (currentRead < 0) {
                    throw new IOException("对端关闭连接");
                }
                nbRead += currentRead;
            }
            //读\r\n
            readLineFromStream();
            return true;
        }
    }
    //读一行内容
    private String readLineFromStream() throws IOException {
        StringBuffer sb = new StringBuffer();
        while (true) {
            int ch = super.read();
            if (ch < 0) {
                if (sb.length() == 0) {
                    return null;
                } else {
                    break;
                }
            } else if (ch == '\r') {
                continue;
            } else if (ch == '\n') {
                break;
            }
            sb.append((char)ch);
        }
        return sb.toString();
    }
}
